// ------------------------------------------------------------------------------
// 
// Copyright (c) 2008-2009 Swampware, Inc.
// 
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
// 
// ------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Text;

namespace Bamboo.Parsing.Generators.CPlusPlus
{
	public class ParserGenerator
	{

		public static void GenerateHeader(string name, string multinamespace, Bamboo.Parsing.Grammars.Grammar grammar, Surf.Set PREDICT, System.IO.TextWriter writer)
		{
			writer.WriteLine("//");
			writer.WriteLine("// " + name + "Parser.h");
			writer.WriteLine("//");
			writer.WriteLine("// AUTOGENERATED " + System.DateTime.Now + "");
			writer.WriteLine("//");
			writer.WriteLine("");
			writer.WriteLine("#ifndef " + name.ToUpper() + "PARSER_H");
			writer.WriteLine("#define " + name.ToUpper() + "PARSER_H");
			writer.WriteLine("");
			writer.WriteLine("#include <istream>");
			writer.WriteLine("#include <vector>");
			writer.WriteLine("#include \"" + name + "Token.h\"");
			writer.WriteLine("");

			string[] namespaces = multinamespace.Split(new string[] { "::" }, StringSplitOptions.None);
			foreach (string nspace in namespaces)
			{
				writer.WriteLine("namespace " + nspace + "");
				writer.WriteLine("{");
			}

			writer.WriteLine("	class " + name + "Parser");
			writer.WriteLine("	{");
			writer.WriteLine("	private:");
			writer.WriteLine("		int _currentState;");
			writer.WriteLine("		std::vector<int> _states;");
			writer.WriteLine("	public:");
			writer.WriteLine("		Parser() : _currentState(0) { }");
			writer.WriteLine("		virtual ~" + name + "Parser() { }");
			writer.WriteLine("");
			writer.WriteLine("		void next(" + name + "Token&);");
			writer.WriteLine("");

			Dictionary<string, string> constructors = new Dictionary<string, string>();
			foreach (string nonterminal in grammar.Nonterminals)
			{
				foreach (Surf.Tuple tuple in PREDICT)
				{
					int n = (int)tuple[0] + 1;
					Bamboo.Parsing.Grammars.Production production = (Bamboo.Parsing.Grammars.Production)tuple[1];
					Surf.Set predict = (Surf.Set)tuple[2];

					if (production.Nonterminal.Equals(nonterminal))
					{
						foreach (string symbol in predict)
						{
							List<Bamboo.Parsing.Grammars.Expression> expressions = ToList(production.Expression);
							if (expressions.Count == 1 && ((Bamboo.Parsing.Grammars.Symbol)expressions[0]).Token == "EPSILON")
							{
							}
							else
							{
								string constructor2 = "		virtual void beginParse" + nonterminal;
								foreach (Bamboo.Parsing.Grammars.Symbol expression in expressions)
									constructor2 += "_" + expression.Token;
								constructor2 += "() = 0;";

								string constructor = "		virtual void endParse" + nonterminal;
								foreach (Bamboo.Parsing.Grammars.Symbol expression in expressions)
									constructor += "_" + expression.Token;
								constructor += "() = 0;";

								if (!constructors.ContainsKey(constructor))
								{
									writer.WriteLine(constructor2);
									writer.WriteLine(constructor);
									constructors.Add(constructor, constructor);
								}
							}
						}
					}
				}
			}
			writer.WriteLine("");
			foreach (string terminal in grammar.Terminals)
			{
				if (terminal != "EPSILON")
				{
					writer.WriteLine("		virtual void parse" + terminal + "(" + name + "Token&) = 0;");
				}
			}
			writer.WriteLine("	};");

			foreach (string nspace in namespaces)
			{
				writer.WriteLine("}");
			}
			writer.WriteLine("#endif");
		}

		public static void GenerateClass(string name, string multinamespace, Bamboo.Parsing.Grammars.Grammar grammar, Surf.Set FIRST, Surf.Set FOLLOW, Surf.Set PREDICT, System.IO.TextWriter writer)
		{
			writer.WriteLine("//");
			writer.WriteLine("// " + name + "Parser.cpp");
			writer.WriteLine("//");
			writer.WriteLine("// AUTOGENERATED " + System.DateTime.Now + "");
			writer.WriteLine("//");
			writer.WriteLine("");
			writer.WriteLine("#include <istream>");
			writer.WriteLine("#include <sstream>");
			writer.WriteLine("#include <string>");
			writer.WriteLine("#include <vector>");
			writer.WriteLine("#include \"" + name + "Parser.h\"");
			writer.WriteLine("#include \"" + name + "Token.h\"");
			writer.WriteLine("#include \"" + name + "TokenType.h\"");
			writer.WriteLine("");

			string[] namespaces = multinamespace.Split(new string[] { "::" }, StringSplitOptions.None);
			foreach (string nspace in namespaces)
			{
				writer.WriteLine("namespace " + nspace + "");
				writer.WriteLine("{");
			}

			writer.WriteLine("	void Parser::next(" + name + "Token& token)");
			writer.WriteLine("	{");
			writer.WriteLine("	start:");
			writer.WriteLine("		switch(_currentState)");
			writer.WriteLine("		{");

			Dictionary<string, int> states = new Dictionary<string, int>();
			int state = 0;
			foreach (string nonterminal in grammar.Nonterminals)
			{
				states.Add(nonterminal, state++);
			}
			foreach (string terminal in grammar.Terminals)
			{
				if (terminal != "EPSILON")
				{
					states.Add(terminal, state++);
				}
			}

			foreach (string nonterminal in grammar.Nonterminals)
			{
				bool hasDefault = false;

				writer.WriteLine("		case " + states[nonterminal] + ": // parse" + nonterminal);
				writer.WriteLine("			{");
				writer.WriteLine("				switch(token.type())");
				writer.WriteLine("				{");

				int state2 = state;
				foreach (Surf.Tuple tuple in PREDICT)
				{
					int n = (int)tuple[0] + 1;
					Bamboo.Parsing.Grammars.Production production = (Bamboo.Parsing.Grammars.Production)tuple[1];
					Surf.Set predict = (Surf.Set)tuple[2];

					if (production.Nonterminal.Equals(nonterminal))
					{
						foreach (string symbol in predict)
						{
							List<Bamboo.Parsing.Grammars.Expression> expressions = ToList(production.Expression);
							if (expressions.Count == 1 && ((Bamboo.Parsing.Grammars.Symbol)expressions[0]).Token == "EPSILON")
							{
								hasDefault = true;
							}
							else
							{
								writer.WriteLine("				case TokenType::" + symbol + ":");
								writer.WriteLine("					{");

								writer.Write("						beginParse" + nonterminal);
								foreach (Bamboo.Parsing.Grammars.Symbol expression in expressions)
									writer.Write("_" + expression.Token);
								writer.WriteLine("();");

								writer.Write("						_states.push_back(" + state2++ + "); // endParse" + nonterminal);
								foreach (Bamboo.Parsing.Grammars.Symbol expression in expressions)
								{
									writer.Write("_" + expression.Token);
								}
								writer.WriteLine("();");
								for (int i = (expressions.Count - 1); i >= 1; i--)
								{
									Bamboo.Parsing.Grammars.Symbol expression = (Bamboo.Parsing.Grammars.Symbol)expressions[i];
									writer.WriteLine("						_states.push_back(" + states[expression.Token] + "); // parse" + expression.Token);
								}
								Bamboo.Parsing.Grammars.Symbol expression2 = (Bamboo.Parsing.Grammars.Symbol)expressions[0];
								writer.WriteLine("						_currentState = " + states[expression2.Token] + "; // parse" + expression2.Token);
								writer.WriteLine("						goto start;");
								writer.WriteLine("						break;");
								writer.WriteLine("					}");
							}
						}
					}
				}

				if (states[nonterminal] == 0)
				{
					writer.WriteLine("				case " + name + "TokenType::_EOF_:");
					writer.WriteLine("					{");
					writer.WriteLine("						if (_states.size() > 0)");
					writer.WriteLine("						{");
					writer.WriteLine("							_currentState = _states.back();");
					writer.WriteLine("							_states.pop_back();");
					writer.WriteLine("							goto start;");
					writer.WriteLine("						}");
					writer.WriteLine("						break;");
					writer.WriteLine("					}");
				}

				if (hasDefault)
				{
					writer.WriteLine("				default:");
					writer.WriteLine("					{");
					writer.WriteLine("						// EPSILON");
					writer.WriteLine("						_currentState = _states.back();");
					writer.WriteLine("						_states.pop_back();");
					writer.WriteLine("						goto start;");
					writer.WriteLine("					}");
				}
				else
				{
					writer.WriteLine("				default:");
					writer.WriteLine("					{");
					writer.WriteLine("						std::stringstream stream;");
					writer.WriteLine("						stream << __FILE__ << \" \" << __LINE__ << \" \" << \"Syntax error.\";");
					writer.WriteLine("						throw stream.str();");
					writer.WriteLine("					}");
				}
				writer.WriteLine("				}");
				writer.WriteLine("				break;");
				writer.WriteLine("			}");



				state2 = state;
				foreach (Surf.Tuple tuple in PREDICT)
				{
					int n = (int)tuple[0] + 1;
					Bamboo.Parsing.Grammars.Production production = (Bamboo.Parsing.Grammars.Production)tuple[1];
					Surf.Set predict = (Surf.Set)tuple[2];

					if (production.Nonterminal.Equals(nonterminal))
					{
						foreach (string symbol in predict)
						{
							List<Bamboo.Parsing.Grammars.Expression> expressions = ToList(production.Expression);
							if (expressions.Count == 1 && ((Bamboo.Parsing.Grammars.Symbol)expressions[0]).Token == "EPSILON")
							{
							}
							else
							{
								writer.Write("		case " + state2++ + ": // endParse" + nonterminal);
								foreach (Bamboo.Parsing.Grammars.Symbol expression in expressions)
									writer.Write("_" + expression.Token);
								writer.WriteLine("();");
								writer.WriteLine("			{");
								writer.Write("				" + "endParse" + nonterminal);
								foreach (Bamboo.Parsing.Grammars.Symbol expression in expressions)
									writer.Write("_" + expression.Token);
								writer.WriteLine("();");
								writer.WriteLine("				if (_states.size() == 0)");
								writer.WriteLine("				{");
								writer.WriteLine("					_currentState = 0;");
								writer.WriteLine("				}");
								writer.WriteLine("				else");
								writer.WriteLine("				{");
								writer.WriteLine("					_currentState = _states.back();");
								writer.WriteLine("					_states.pop_back();");
								writer.WriteLine("				}");
								writer.WriteLine("				goto start;");
								writer.WriteLine("				break;");
								writer.WriteLine("			}");
							}
						}
					}
				}

				state = state2;
			}
			foreach (string terminal in grammar.Terminals)
			{
				if (terminal != "EPSILON")
				{
					writer.WriteLine("		case " + states[terminal] + ": // " + terminal);
					writer.WriteLine("			{");
					writer.WriteLine("				parse" + terminal + "(token);");
					writer.WriteLine("				if (_states.size() == 0)");
					writer.WriteLine("				{");
					writer.WriteLine("					_currentState = 0;");
					writer.WriteLine("				}");
					writer.WriteLine("				else");
					writer.WriteLine("				{");
					writer.WriteLine("					_currentState = _states.back();");
					writer.WriteLine("					_states.pop_back();");
					writer.WriteLine("				}");
					writer.WriteLine("				break;");
					writer.WriteLine("			}");
				}
			}

			writer.WriteLine("		default:");
			writer.WriteLine("			{");
			writer.WriteLine("				std::stringstream stream;");
			writer.WriteLine("				stream << __FILE__ << \" \" << __LINE__ << \" \" << \"Invalid parser state.\";");
			writer.WriteLine("				throw stream.str();");
			writer.WriteLine("			}");
			writer.WriteLine("		}");
			writer.WriteLine("	}");
			writer.WriteLine("");

			foreach (string nspace in namespaces)
			{
				writer.WriteLine("}");
			}
		}

		private static List<Bamboo.Parsing.Grammars.Expression> ToList(Bamboo.Parsing.Grammars.Expression expression)
		{
			List<Bamboo.Parsing.Grammars.Expression> list = new List<Bamboo.Parsing.Grammars.Expression>();
			if (expression is Bamboo.Parsing.Grammars.Symbol)
			{
				Bamboo.Parsing.Grammars.Symbol symbol = (Bamboo.Parsing.Grammars.Symbol)expression;
				list.Add(symbol);
			}
			else if (expression is Bamboo.Parsing.Grammars.Concatenation)
			{
				Bamboo.Parsing.Grammars.Concatenation concatenation = (Bamboo.Parsing.Grammars.Concatenation)expression;
				foreach (Bamboo.Parsing.Grammars.Symbol symbol in concatenation.Expressions)
				{
					list.Add(symbol);
				}
			}
			else
			{
				throw new System.Exception("INVALID");
			}
			return list;
		}

		private ParserGenerator()
		{
		}

	}
}
